use crate::constants::MI_GiB;

pub const mi_max_error_count  : u64 = 16; // stop outputting errors after this (use < 0 for no limit)
pub const mi_max_warning_count: u64 = 16; // stop outputting warnings after this (use < 0 for no limit)


// --------------------------------------------------------
// Options
// These can be accessed by multiple threads and may be
// concurrently initialized, but an initializing data race
// is ok since they resolve to the same value.
// --------------------------------------------------------
pub enum mi_init_e {
  UNINIT,       // not yet initialized
  DEFAULTED,    // not found in the environment, use default value
  INITIALIZED   // found in environment or set explicitly
}

pub struct mi_option_desc {
  pub value: i64,  // the value
  pub init: mi_init_e,   // is it initialized yet? (from the environment)
}

pub enum mi_option_e {
    // stable options
    mi_option_show_errors,                // print error messages
    mi_option_show_stats,                 // print statistics on termination
    mi_option_verbose,                    // print verbose messages
    // advanced options
    mi_option_eager_commit,               // eager commit segments? (after `eager_commit_delay` segments) (=1)
    mi_option_arena_eager_commit,         // eager commit arenas? Use 2 to enable just on overcommit systems (=2)
    mi_option_purge_decommits,            // should a memory purge decommit? (=1). Set to 0 to use memory reset on a purge (instead of decommit)
    mi_option_allow_large_os_pages,       // allow large (2 or 4 MiB) OS pages, implies eager commit. If false, also disables THP for the process.
    mi_option_reserve_huge_os_pages,      // reserve N huge OS pages (1GiB pages) at startup
    mi_option_reserve_huge_os_pages_at,   // reserve huge OS pages at a specific NUMA node
    mi_option_reserve_os_memory,          // reserve specified amount of OS memory in an arena at startup (internally, this value is in KiB; use `mi_option_get_size`)
    mi_option_deprecated_segment_cache,
    mi_option_deprecated_page_reset,
    mi_option_abandoned_page_purge,       // immediately purge delayed purges on thread termination
    mi_option_deprecated_segment_reset,
    mi_option_eager_commit_delay,         // the first N segments per thread are not eagerly committed (but per page in the segment on demand)
    mi_option_purge_delay,                // memory purging is delayed by N milli seconds; use 0 for immediate purging or -1 for no purging at all. (=10)
    mi_option_use_numa_nodes,             // 0 = use all available numa nodes, otherwise use at most N nodes.
    mi_option_disallow_os_alloc,          // 1 = do not use OS memory for allocation (but only programmatically reserved arenas)
    mi_option_os_tag,                     // tag used for OS logging (macOS only for now) (=100)
    mi_option_max_errors,                 // issue at most N error messages
    mi_option_max_warnings,               // issue at most N warning messages
    mi_option_max_segment_reclaim,        // max. percentage of the abandoned segments can be reclaimed per try (=10%)
    mi_option_destroy_on_exit,            // if set, release all memory on exit; sometimes used for dynamic unloading but can be unsafe
    mi_option_arena_reserve,              // initial memory size for arena reservation (= 1 GiB on 64-bit) (internally, this value is in KiB; use `mi_option_get_size`)
    mi_option_arena_purge_mult,           // multiplier for `purge_delay` for the purging delay for arenas (=10)
    mi_option_purge_extend_delay,
    mi_option_abandoned_reclaim_on_free,  // allow to reclaim an abandoned segment on a free (=1)
    mi_option_disallow_arena_alloc,       // 1 = do not use arena's for allocation (except if using specific arena id's)
    mi_option_retry_on_oom,               // retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries. (only on windows)
    mi_option_visit_abandoned,            // allow visiting heap blocks from abandoned threads (=0)
    mi_option_guarded_min,                // only used when building with MI_GUARDED: minimal rounded object size for guarded objects (=0)
    mi_option_guarded_max,                // only used when building with MI_GUARDED: maximal rounded object size for guarded objects (=0)
    mi_option_guarded_precise,            // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0)
    mi_option_guarded_sample_rate,        // 1 out of N allocations in the min/max range will be guarded (=1000)
    mi_option_guarded_sample_seed,        // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0)
    mi_option_target_segments_per_thread, // experimental (=0)
    _mi_option_last,
}

impl mi_option_e {
    pub const fn to_num(self) -> usize {
        match self {
            mi_option_e::mi_option_show_errors => 0,
            mi_option_e::mi_option_show_stats => 1,
            mi_option_e::mi_option_verbose => 2,
            mi_option_e::mi_option_eager_commit => 3,
            mi_option_e::mi_option_arena_eager_commit => 4,
            mi_option_e::mi_option_purge_decommits => 5,
            mi_option_e::mi_option_allow_large_os_pages => 6,
            mi_option_e::mi_option_reserve_huge_os_pages => 7,
            mi_option_e::mi_option_reserve_huge_os_pages_at => 8,
            mi_option_e::mi_option_reserve_os_memory => 9,
            mi_option_e::mi_option_deprecated_segment_cache => 10,
            mi_option_e::mi_option_deprecated_page_reset => 11,
            mi_option_e::mi_option_abandoned_page_purge => 12,
            mi_option_e::mi_option_deprecated_segment_reset => 13,
            mi_option_e::mi_option_eager_commit_delay => 14,
            mi_option_e::mi_option_purge_delay => 15,
            mi_option_e::mi_option_use_numa_nodes => 16,
            mi_option_e::mi_option_disallow_os_alloc => 17,
            mi_option_e::mi_option_os_tag => 18,
            mi_option_e::mi_option_max_errors => 19,
            mi_option_e::mi_option_max_warnings => 20,
            mi_option_e::mi_option_max_segment_reclaim => 21,
            mi_option_e::mi_option_destroy_on_exit => 22,
            mi_option_e::mi_option_arena_reserve => 23,
            mi_option_e::mi_option_arena_purge_mult => 24,
            mi_option_e::mi_option_purge_extend_delay => 25,
            mi_option_e::mi_option_abandoned_reclaim_on_free => 26,
            mi_option_e::mi_option_disallow_arena_alloc => 27,
            mi_option_e::mi_option_retry_on_oom => 28,
            mi_option_e::mi_option_visit_abandoned => 29,
            mi_option_e::mi_option_guarded_min => 30,
            mi_option_e::mi_option_guarded_max => 31,
            mi_option_e::mi_option_guarded_precise => 32,
            mi_option_e::mi_option_guarded_sample_rate => 33,
            mi_option_e::mi_option_guarded_sample_seed => 34,
            mi_option_e::mi_option_target_segments_per_thread => 35,
            mi_option_e::_mi_option_last => 36,
        }
    }
    
}
// Some options can be set at build time for statically linked libraries
// (use `-DMI_EXTRA_CPPDEFS="opt1=val1;opt2=val2"`)
//
// This is useful if we cannot pass them as environment variables
// (and setting them programmatically would be too late)

pub const MI_DEFAULT_VERBOSE: i64 = 0;

pub const MI_DEFAULT_EAGER_COMMIT: i64 = 1;

pub const MI_DEFAULT_ARENA_EAGER_COMMIT: i64 = 2;


// in KiB
pub const MI_DEFAULT_ARENA_RESERVE: i64 = if size_of::<usize>() > 4 { 1024 * 1024 } else { 128 * 1024 };

pub const MI_DEFAULT_DISALLOW_ARENA_ALLOC: i64 = 0;

pub const MI_DEFAULT_ALLOW_LARGE_OS_PAGES: i64 = 0;

pub const MI_DEFAULT_RESERVE_HUGE_OS_PAGES: i64 = 0;

pub const MI_DEFAULT_RESERVE_OS_MEMORY: i64 = 0;

pub const MI_DEFAULT_GUARDED_SAMPLE_RATE: i64 = 0;


pub const options: [mi_option_desc; mi_option_e::_mi_option_last.to_num()] =
[
  // stable options
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: MI_DEFAULT_VERBOSE, init: mi_init_e::UNINIT },

  // some of the following options are experimental and not all combinations are allowed.
  
  mi_option_desc { value: MI_DEFAULT_EAGER_COMMIT, init: mi_init_e::UNINIT },               // commit per segment directly (4MiB)  (but see also `eager_commit_delay`)
  mi_option_desc { value: MI_DEFAULT_ARENA_EAGER_COMMIT, init: mi_init_e::UNINIT }, // eager commit arena's? 2 is used to enable this only on an OS that has overcommit (i.e. linux)
  mi_option_desc { value: 1, init: mi_init_e::UNINIT },      // purge decommits memory (instead of reset) (note: on linux this uses MADV_DONTNEED for decommit)
  mi_option_desc { value: MI_DEFAULT_ALLOW_LARGE_OS_PAGES, init: mi_init_e::UNINIT },    // use large OS pages, use only with eager commit to prevent fragmentation of VMA's
  mi_option_desc { value: MI_DEFAULT_RESERVE_HUGE_OS_PAGES, init: mi_init_e::UNINIT },      // per 1GiB huge pages
  mi_option_desc { value: -1, init: mi_init_e::UNINIT },   // reserve huge pages at node N
  mi_option_desc { value: MI_DEFAULT_RESERVE_OS_MEMORY, init: mi_init_e::UNINIT },      // reserve N KiB OS memory in advance (use `option_get_size`)
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },
  mi_option_desc { value: 1, init: mi_init_e::UNINIT },
  mi_option_desc { value: 10, init: mi_init_e::UNINIT },  // purge delay in milli-seconds
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },           // 0 = use available numa nodes, otherwise use at most N nodes.
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },           // 1 = do not use OS memory for allocation (but only reserved arenas)
  mi_option_desc { value: 100, init: mi_init_e::UNINIT },                   // only apple specific for now but might serve more or less related purpose
  mi_option_desc { value: 32, init: mi_init_e::UNINIT },               // maximum errors that are output
  mi_option_desc { value: 32, init: mi_init_e::UNINIT },             // maximum warnings that are output
  mi_option_desc { value: 10, init: mi_init_e::UNINIT },       // max. percentage of the abandoned segments to be reclaimed per try.
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },           // release all OS memory on process exit; careful with dangling pointer or after-exit frees!
  mi_option_desc { value: MI_DEFAULT_ARENA_RESERVE, init: mi_init_e::UNINIT }, // reserve memory N KiB at a time (=1GiB) (use `option_get_size`)
  mi_option_desc { value: 10, init: mi_init_e::UNINIT },         // purge delay multiplier for arena's
  mi_option_desc { value: 1, init: mi_init_e::UNINIT },
  mi_option_desc { value: 1, init: mi_init_e::UNINIT },// reclaim an abandoned segment on a free
  mi_option_desc { value: MI_DEFAULT_DISALLOW_ARENA_ALLOC, init: mi_init_e::UNINIT }, // 1 = do not use arena's for allocation (except if using specific arena id's)
  mi_option_desc { value: 400, init: mi_init_e::UNINIT },             // windows only: retry on out-of-memory for N milli seconds (=400), set to 0 to disable retries.
  mi_option_desc { value: 0, init: mi_init_e::UNINIT }, // allow visiting heap blocks in abandonded segments; requires taking locks during reclaim.
  mi_option_desc { value: 0, init: mi_init_e::UNINIT }, // only used when building with MI_GUARDED: minimal rounded object size for guarded objects
  mi_option_desc { value: MI_GiB as i64, init: mi_init_e::UNINIT },         // only used when building with MI_GUARDED: maximal rounded object size for guarded objects
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },           // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0)
  mi_option_desc { value: MI_DEFAULT_GUARDED_SAMPLE_RATE, init: mi_init_e::UNINIT },       // 1 out of N allocations in the min/max range will be guarded (=4000)
  mi_option_desc { value: 0, init: mi_init_e::UNINIT }, 
  mi_option_desc { value: 0, init: mi_init_e::UNINIT },  // abandon segments beyond this point, or 0 to disable.
];